function _(id){ return document.getElementById(id); } /** * @description 跨浏览器getElementsByClassName实现 * @param {String} searchClass '{tag}.{className}' * @param {HTMLElement} node 搜索的的目标父节点 * @returns {Array(HTMLElement)} 数组 */ _._ = function (searchClass, node) { var result = [], sc = searchClass.split('.'), tag; if(sc.length ===2){ searchClass = sc[1]; tag = sc[0]||'*'; }else{ return null; } if(document.getElementsByClassName){ var nodes = (node || document).getElementsByClassName(searchClass); for(var i=0 ;node = nodes[i++];){ if(tag !== "*" && node.tagName === tag.toUpperCase()){ result.push(node); }else{ result.push(node); } } }else{ if ( node == null ) node = document; var els = node.getElementsByTagName(tag); var elsLen = els.length; var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)"); for (i = 0, j = 0; i < elsLen; i++) { if ( pattern.test(els[i].className) ) { result[j] = els[i]; j++; } } } return result; }; /** * 对Date的扩展,将 Date 转化为指定格式的String * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符 * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) * eg: * (new Date()).pattern("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 * (new Date()).pattern("yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04 * (new Date()).pattern("yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04 * (new Date()).pattern("yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04 * (new Date()).pattern("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 */ Date.prototype.pattern = function(fmt) { var o = { "M+" : this.getMonth()+1, //月份 "d+" : this.getDate(), //日 "h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时 "H+" : this.getHours(), //小时 "m+" : this.getMinutes(), //分 "s+" : this.getSeconds(), //秒 "q+" : Math.floor((this.getMonth()+3)/3), //季度 "S" : this.getMilliseconds() //毫秒 }; var week = { "0" : "\u65e5", "1" : "\u4e00", "2" : "\u4e8c", "3" : "\u4e09", "4" : "\u56db", "5" : "\u4e94", "6" : "\u516d" }; if(/(y+)/.test(fmt)){ fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length)); } if(/(E+)/.test(fmt)){ fmt=fmt.replace(RegExp.$1, ((RegExp.$1.length>1) ? (RegExp.$1.length>2 ? "\u661f\u671f" : "\u5468") : "")+week[this.getDay()+""]); } for(var k in o){ if(new RegExp("("+ k +")").test(fmt)){ fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); } } return fmt; } /** * @description 判断是否是IE,如果是返回具体版本号 * @returns IE的版本号,W3C系列返回undefined * */ _.isIE = (function(){ var undef, v = 3, div = document.createElement('div'), all = div.getElementsByTagName('i'); while ( div.innerHTML = '', all[0] ); return v > 4 ? v : undef; }()); /** * @description 判断是否是数组 */ _.isArray = function(o){ return Object.prototype.toString.call(o) === '[object Array]'; }; /** * @description 去除字符串的首尾空白 */ _.trim = function(text){ if(String.prototype.trim){ return text == null ?"" :String.prototype.trim.call(text); }else{ return text == null ?"" :text.toString().replace( /^\s+/, "" ).replace( /\s+$/, "" ); } }; /** * @description 序列化对象为查询字符串 * @param {Object} obj 待序列化的对象,如{a:1,b:2,c:0,d:false,e:null},0和false返回对应的字符串,null和undefined返回空 * @returns {String} 形如'a=1&b=2&c=0&d=false&e='的字符串 */ _.serialize = function(obj){ if(typeof obj != 'object') return ''; var ret = []; for(var key in obj){ ret.push(key + '=' + (obj[key] || (_.contains([0,false],obj[key])?String(obj[key]):''))); } return ret.join('&'); }; _.contains = function(array,item){ return _.indexOf(array,item) != -1; }; _.indexOf = function(array,item){ var retIdx = -1; if(!array || !array.length) return retIdx; for(var index in array){ if(item === array[index]){ retIdx = index; break; } } return parseInt(retIdx); }; /** * @description from jquery.cookie.js * @param {Object} options {expires,path,domain,secure} */ _.cookie = function(name,value,options){ if (typeof value != 'undefined') { // name and value given, set cookie options = options || {}; if (value === null) { value = ''; options.expires = -1; } var expires = ''; if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { var date; if (typeof options.expires == 'number') { date = new Date(); date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); } else { date = options.expires; } expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE } var path = options.path ? '; path=' + options.path : ''; var domain = options.domain ? '; domain=' + options.domain : ''; var secure = options.secure ? '; secure' : ''; document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); } else { // only name given, get cookie var cookieValue = ''; if (document.cookie && document.cookie != '') { var cookies = document.cookie.split(';'); for (var i = 0; i < cookies.length; i++) { var cookie = _.trim(cookies[i]); // Does this cookie string begin with the name we want? if (cookie.substring(0, name.length + 1) == (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } }; _.url = function(url){ var a = document.createElement('a'); a.href = url; return { source: url, protocol: a.protocol.replace(':',''), host: a.hostname, port: a.port, query: a.search, params: (function(){ var ret = {}, seg = a.search.replace(/^\?/,'').split('&'), len = seg.length, i = 0, s; for (;i -1) { requestUrl = url.replace("×tamp=false", "") + (url.indexOf('?') > -1 && queryStringObj ? '&' + _.serialize(queryStringObj) : (queryStringObj ? ('&' + _.serialize(queryStringObj)) : '')); } else { requestUrl = url + (url.indexOf('?') > -1 ? '×tamp=' : '?timestamp=') + now + (queryStringObj ? ('&' + _.serialize(queryStringObj)) : ''); } if(_.isIE && _.isIE < 9){ script = document.getElementById(_.ajax.SCRIPT_ID); //节点被占用 if(_.ajax.SCRIPT_USED){ if(_.ajax.LAST_USED_TIME === 0) _.ajax.LAST_USED_TIME = now; //已经超时 if((now - _.ajax.LAST_USED_TIME) > _.ajax.TIMEOUT){ _.ajax.LAST_USED_TIME = now; if(_.ajax.TIMEOUT_REQUEST.length>=100) _.ajax.TIMEOUT_REQUEST.length = 0; _.ajax.TIMEOUT_REQUEST.push(script.src.split('×tamp=')[0]); script.src = requestUrl; //没有超时则等待 }else{ setTimeout(function(){_.ajax.jsonp(url);},_.ajax.WAIT_TIME); } }else{ _.ajax.SCRIPT_USED = true; _.ajax.LAST_USED_TIME = now; script.src = requestUrl; } }else{ script = document.createElement('script'); head.appendChild(script); script.onload = function(){ this.onload = null; this.parentNode.removeChild(this); }; //opera 不支持onerror事件,不过无关紧要 script.onerror = function(){ _.ajax.TIMEOUT_REQUEST.push(this.src); }; script.src = requestUrl; } }; /** * 支持错误回调的jsonp方法 * @params * { url:'',//请求地址 data:{}||'',//请求参数,支持json或者string callback:function(){},//请求成功回调函数 context:null,//修正回调函数的执行上下文 error:function(){},//请求错误回调函数 jsonpkey:'callback',//回调参数名 cache:false//默认不使用缓存 } */ _.ajax.jsonp2 = function(opts){ var defaults = { url:'', data:{}, callback:function(){}, context:null, error:function(){}, jsonpkey:'callback', cache:false },mix = function(target, source ,override) { var i, ride = (override === void 0) || override; for (i in source) { if (ride || !(i in target)) { target[i] = source[i]; } } return target; }, opts = mix(defaults,opts,true); var script=document.createElement("script"), add = /\?/.test( opts.url ) ? "&" : "?", query = [], jsonpname = "callback"+1*new Date, jsonpval = "_.ajax.jsonp2."+jsonpname, self = arguments.callee; if(typeof opts.data=='string'){ opts.url = opts.url+add+opts.data+'&'+opts.jsonpkey+'='+jsonpval+(opts.cache?'':'&jsonptimestamp'+new Date().getTime()); }else if(typeof opts.data=='object'){ opts.data[opts.jsonpkey] = jsonpval; !opts.cache && (opts.data['jsonptimestamp']=new Date().getTime()); for(var i in opts.data){ query.push(i+'='+opts.data[i]); } opts.url = opts.url+add+query.join("&"); } self[jsonpname] = function(json) { script.jsonp = 1; if(opts.context){ opts.callback.apply(opts.context,[json]); }else{ opts.callback(json); } }; script.src = opts.url; var fn = function(){ if( -[1,] || /loaded|complete/i.test(this.readyState)){ self._removeScript(this,opts,jsonpname); } }; //ie9/10两个都支持,要区分,不然执行两次 if(_.isIE && _.isIE < 9){ script.onreadystatechange = fn; }else{ script.onload = fn; } script.onerror = function(){ self._removeScript(this,opts,jsonpname); }; var head = document.getElementsByTagName("head")[0]; head.appendChild(script); mix(self,{ _removeScript : function(script, opts, jsonpname) { if (typeof script.jsonp === "undefined") { opts.error(opts); } if (script.clearAttributes) { script.clearAttributes(); } else { script.onload = script.onreadystatechange = script.onerror = null; } script.parentNode.removeChild(script); delete this[jsonpname]; } }); }; /** * @description 获取远程脚本,只适合加载一次的脚本,轮询请使用_.ajax.jsonp。 * @param {Function} callback 加载脚本成功之后的回调函数 */ _.ajax.getScript = function(url,callback){ var script = document.createElement('script'), head = document.head || document.getElementsByTagName('head')[0], cbType = typeof(callback); if(cbType=='function'){ if(script.readyState){ script.onreadystatechange = function(){ if (this.readyState == "loaded" || this.readyState == "complete"){ callback(); } }; } else{ script.onload = callback; } } script.src = url; head.appendChild(script); }; /** * @description 获取多个远程脚本文件,只适合加载一次。轮询请使用_.ajax.jsonp */ _.ajax.getScripts = function(urlArray){ if(_.isArray(urlArray)){ for(var index in urlArray){ _.ajax.getScript(urlArray[index]); } } }; /** * @description 网络模块 * @property {Number} DETECT_CYCLE 切换到备用服务器后,监测主域名的周期,单位为秒,周期长度最好不要设置为10的倍数,便于调试观察 */ _.net = { DETECT_CYCLE:55 }; /** * @description 为跨域请求函数注入容灾备份机制,需要将原函数的请求服务器地址改成_.net['funcname_current_server']; * 实现原理:使用对象存储每一次请求使用的服务器地址和请求结果,然后在每一次请求之前,判断前两次使用相同服务器的请求是否都不成功。 * 如果都不成功表明连续两次请求失败则切换到备用服务器 * @param {Object} entry 入口函数{context:执行环境,name:函数名称} * @param {Object} exit 回调函数{context:执行环境,name:函数名称} * @param {Object} settings 服务端配置{server:主域地址,backupServer:备选服务器地址,数组,长度为0则不启用容灾机制,serverImg:监测主域名可用的图片} * @param {Function} forward 动态判断是否在入口函数继续执行注入的容灾机制 * @usage 参见/user/js/map.js * @dependacy {Function} _.inject */ _.net.backup = function(entry,exit,settings,forward){ var server = settings.server,currentServer = entry.name + '_current_server'; _.net[currentServer] = server; //没有备用地址则不启动容灾备份机制 if(!settings.backupServer || !_.isArray(settings.backupServer) || !settings.backupServer.length) return; var interval = entry.name + '_interval',//切换到备用以后发起定时器key_name total = entry.name + '_total',//总的请求次数key_name status = entry.name + '_status',//维护各次请求的地址和请求结果key_name serverAvailable = entry.name + '_server_available';//表明当前主服务器是否可用的key_name //主服务器可用时立即切换,并重置对应的变量 var resetServer = function(){ _.net[serverAvailable] = true; _.net[currentServer] = server; clearInterval(_.net[interval]); }; //筛选服务器算法,如果当前使用主服务器则切换到备用,如果已经是备用,有多个备用时则切换到下一个备用,只有一个备用是切换到主服务器 var selectServer = function(){ if(_.net[currentServer] == server){ return settings.backupServer[0]; }else{ var len = settings.backupServer.length; if(len ==1) return server; var index = _.indexOf(settings.backupServer,_.net[currentServer]); return settings.backupServer[index+1] || settings.backupServer[0]; } }; _.net[serverAvailable] = true; _.net[interval] = null; _.net[total] = 0; _.net[status] = {};//格式:{1:['http://tbo.desn.net/',true], 2:['http://tbo.desn.net/']} entry.start = function(){ if(typeof forward == 'function'){ if(!forward())return; } _.net[total] += 1; var count = _.net[total]; //存储当此请求使用的服务器地址和响应结果 _.net[status][count-1] = []; if(count > 2){ var prev = _.net[status][count-2], prevPrev = _.net[status][count-3]; //上两次请求使用同一个服务器且都失败了,则切换服务器 if(prev[0] == prevPrev[0] && !prev[1] && !prevPrev[1]){ var usingServer = _.net[currentServer] == server, old = _.net[currentServer]; if(usingServer){ //始终确保只有一个定时器在工作,以避免时间长了会多次请求verify.gif if(_.net[interval]){ clearInterval(_.net[interval]); } _.net[interval] = setInterval(function(){ var img = new Image(); img.onload = resetServer; img.src = settings.serverImg + (settings.serverImg.indexOf('?')>-1?'&':'?') + 't=' + new Date().getTime(); }, _.net.DETECT_CYCLE*1000); } //在切换之前判断主服务器是否正常 如果正常 则不切换,否则切换 var imgMainServer = new Image(); //onload:主服务器正常 无需切换 同时发布一条微博信息 //imgMainServer.onload = function(){ // var msgStop = settings.errStop|| '切换备用已阻止'; // desn.$error(msgStop); //}; //onerror:主服务器异常 切换到备用 imgMainServer.onerror = function(){ //如果只有一个备用服务器且备用不可用则立即切换到tbo if(!usingServer && settings.backupServer.length==1){ resetServer(); }else{ _.net[currentServer] = selectServer(_.net[currentServer]); _.net[serverAvailable] = _.net[currentServer] == server; } if(settings.errorHandler){ settings.errorHandler(old,_.net[currentServer]); } }; imgMainServer.src = settings.serverImg + (settings.serverImg.indexOf('?')>-1?'&':'?') + 't=' + new Date().getTime(); }else{ //如果备用服务器一直正常,那么需要一直检测主域名是否可用,如果可用立即切换到主域名 if(_.net[currentServer] != server){ if(_.net[serverAvailable]){ resetServer(); } } } //删除冗余信息,保证数组长度为2 delete _.net[status][count-3]; } //记录当此请求使用的服务器地址 _.net[status][count-1][0] = _.net[currentServer]; }; exit.start = function(){ //标记本次请求成功,不成功的请求数组长度为1 _.net[status][_.net[total]-1][1] = true; }; _.inject(entry); _.inject(exit); }; /*系统统一信息处理*/ function SystemInfoHandling(data) { if (data == null) return; switch (data.errorCode) { case "401"://体验账户 alert(data.errorDescribe); break; case "403"://MDS过期 alert(data.errorDescribe); break; case "50019"://权限不足 alert(data.errorDescribe); break; } } (function(document){ window.desn = window.desn || {}; var _url = location.href; var _host = _url.indexOf("www.coomix.net")>-1 ? "http://dev.coomix.net" : "http://www.coomix.net"; var errorArr = []; var timerId = null; var delay = 10000;//延伸10秒提交请求 /** * @describ 错误信息上传 * @param msg 必需 错误信息 * @param url 可选 错误行 * @param line 可选 错误列 * @param limit 可选 激活上传的错误总数 */ desn.$error = function(msg,url,line,limit){ //sMsg,sUrl,sLine,limit var arg = arguments; var obj = {}; if(typeof arg[0]==="object"){ if(!arg[0].msg){ throw new Error("Error due to:desn.$error parma obj.msg is need"); }else{ obj = arg[0]; obj.line = obj.line || "?"; obj.url = obj.url || "?"; } }else if(typeof arg[0]==="string"){ obj.msg = arg[0]; obj.url = arg[1] || "?"; obj.line = arg[2] || "?"; obj.limit = arg[3] || undefined; }else{ throw new Error("Error due to:desn.$error parma error"); } errorArr.push(obj); var limit = obj.limit; if(limit){ if(typeof limit !=="number"){ throw new Error("Error due to:parma limit should be a number"); }else if(errorArr.length===limit){ upLoadError(errorArr); } }else{ if(timerId){ clearTimeout(timerId); timerId = null; } timerId = setTimeout(function(){ upLoadError(errorArr); },delay); } }; function upLoadError(arr){ return; //阻止错误上报 if(arr.length){ var content = "#错误信息#\n"; for(var i=0;i